#!/usr/bin/env node

/**
 * Star Resonance DPS Tracker - Unified Launcher
 * 星痕共鸣DPS追踪器 - 统一启动器 by小红猫 古拉哈提亚
 * 
 * 功能：
 * 1. 环境检测和自动修复
 * 2. 依赖安装和验证
 * 3. 自动启动服务器和悬浮窗
 * 4. 错误诊断和恢复
 */

const fs = require('fs');
const path = require('path');
const { spawn, exec, execSync } = require('child_process');
const os = require('os');
const http = require('http');
const net = require('net');
const osPaths = {
    localAppData: process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local')
};

class DPSTrackerLauncher {
    constructor() {
        this.appName = 'Star Resonance DPS Tracker';
        this.version = '2.1.0';
        this.isPackaged = process.pkg !== undefined;
        this.appDir = this.isPackaged ? path.dirname(process.execPath) : __dirname;
        this.logFile = path.join(this.appDir, 'launcher.log');
        this.configFile = this.chooseConfigFilePath();
        
        // 默认配置
        this.config = {
            serverPort: 8989,
            autoStartBrowser: false, // 默认不启动网页端
            autoStartOverlay: true,  // 默认只启动悬浮窗
            logLevel: 'info',
            deviceIndex: 3, // 默认网络设备
            lastDeviceIndex: -1,
            skipIntro: false
        };
        
        this.serverProcess = null;
        this.overlayProcess = null;
        this.overlayStarted = false;
        this.browserStarted = false;
        
        this.loadConfig();
        this.ensureConfigFileExists();
        this.initLog();
    }

    chooseConfigFilePath() {
        try {
            const appDirConfig = path.join(this.appDir, 'launcher_config.json');
            const localDir = path.join(osPaths.localAppData, 'StarDPS');
            try { fs.mkdirSync(localDir, { recursive: true }); } catch {}
            const localConfig = path.join(localDir, 'launcher_config.json');

            // 若应用目录下已有配置文件，优先使用（便携式场景）
            if (fs.existsSync(appDirConfig)) return appDirConfig;

            // 检查应用目录写权限
            try {
                fs.accessSync(this.appDir, fs.constants.W_OK);
                return appDirConfig;
            } catch {
                // 无法写入，fallback 到 LocalAppData
                return localConfig;
            }
        } catch {
            // 极端情况下回退到应用目录
            return path.join(this.appDir, 'launcher_config.json');
        }
    }

    // 日志系统
    initLog() {
        try {
            fs.writeFileSync(this.logFile, `[${this.getTimestamp()}] ${this.appName} v${this.version} 启动\n`);
            fs.appendFileSync(this.logFile, `[${this.getTimestamp()}] 运行模式: ${this.isPackaged ? 'Packaged' : 'Development'}\n`);
            fs.appendFileSync(this.logFile, `[${this.getTimestamp()}] 应用目录: ${this.appDir}\n`);
        } catch (err) {
            console.error('日志初始化失败:', err.message);
        }
    }

    log(message, level = 'INFO') {
        const logMessage = `[${this.getTimestamp()}] [${level}] ${message}`;
        console.log(logMessage);
        try {
            fs.appendFileSync(this.logFile, logMessage + '\n');
        } catch (err) {
            // 忽略日志写入错误，避免阻塞主流程
        }
    }

    getTimestamp() {
        return new Date().toLocaleString('zh-CN');
    }

    // 工具：返回第一个存在的路径
    getFirstExistingPath(candidatePaths) {
        for (const p of candidatePaths) {
            try {
                if (p && fs.existsSync(p)) return p;
            } catch {}
        }
        return null;
    }

    // 配置管理
    loadConfig() {
        try {
            if (fs.existsSync(this.configFile)) {
                const savedConfig = JSON.parse(fs.readFileSync(this.configFile, 'utf8'));
                this.config = { ...this.config, ...savedConfig };
                this.log('配置文件加载成功');

            } 



        } catch (err) {
            this.log(`配置文件加载失败: ${err.message}`, 'WARN');
        }
    }

    ensureConfigFileExists() {
        try {
            if (!fs.existsSync(this.configFile)) {
                fs.writeFileSync(this.configFile, JSON.stringify(this.config, null, 2));
            }
        } catch (err) {
            // 忽略写入失败，后续保存再尝试
        }
    }

    // 定位系统 Node 与 NPM（避免使用 pkg 内置 Node 导致 ABI 冲突）
    findSystemNodeAndNpm() {
        if (os.platform() !== 'win32') {
            // 非 Windows 简化处理
            return { nodeExe: 'node', npmCmd: 'npm' };
        }
        try {
            // 通过 where node 获取真实路径
            const stdout = execSync('cmd /c where node', { encoding: 'utf8' });
            const lines = stdout.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
            for (const p of lines) {
                try {
                    if (fs.existsSync(p) && p.toLowerCase().endsWith('node.exe')) {
                        const npmCandidate = path.join(path.dirname(p), 'npm.cmd');
                        const npmCmd = fs.existsSync(npmCandidate) ? npmCandidate : 'npm.cmd';
                        return { nodeExe: p, npmCmd };
                    }
                } catch {}
            }
        } catch {}

        // 常见安装位置回退
        const candidates = [
            process.env['NVM_SYMLINK'] && path.join(process.env['NVM_SYMLINK'], 'node.exe'),
            path.join(process.env['ProgramFiles'] || 'C\\\\Program Files', 'nodejs', 'node.exe'),
            path.join(process.env['"ProgramFiles(x86)"'] || (process.env['ProgramFiles(x86)'] || 'C\\\\Program Files (x86)'), 'nodejs', 'node.exe')
        ].filter(Boolean);
        for (const p of candidates) {
            try {
                if (fs.existsSync(p)) {
                    const npmCandidate = path.join(path.dirname(p), 'npm.cmd');
                    const npmCmd = fs.existsSync(npmCandidate) ? npmCandidate : 'npm.cmd';
                    return { nodeExe: p, npmCmd };
                }
            } catch {}
        }
        return { nodeExe: 'node', npmCmd: 'npm.cmd' };
    }

    saveConfig() {
        try {
            fs.writeFileSync(this.configFile, JSON.stringify(this.config, null, 2));
            this.log('配置已保存');
        } catch (err) {
            this.log(`配置保存失败: ${err.message}`, 'WARN');
        }
    }

    // 环境检测
    async checkEnvironment() {
        this.log('开始环境检测...');
        
        const checks = [
            { name: 'Node.js版本', check: () => this.checkNodeVersion() },
            { name: '主项目依赖', check: () => this.checkMainDependencies() },
            { name: '网络权限', check: () => this.checkNetworkPermissions() },
            { name: '悬浮窗依赖', check: () => this.checkOverlayDependencies() }
        ];

        let allPassed = true;
        for (const check of checks) {
            try {
                this.log(`检查 ${check.name}...`);
                const result = await check.check();
                if (result.success) {
                    this.log(`✅ ${check.name}: ${result.message || 'OK'}`);
                } else {
                    this.log(`❌ ${check.name}: ${result.message}`, 'ERROR');
                    allPassed = false;
                    
                    // 尝试自动修复
                    if (result.autoFix) {
                        this.log(`🔧 尝试自动修复 ${check.name}...`);
                        const fixResult = await result.autoFix();
                        if (fixResult.success) {
                            this.log(`✅ ${check.name} 修复成功: ${fixResult.message}`);
                        } else {
                            this.log(`❌ ${check.name} 修复失败: ${fixResult.message}`, 'ERROR');
                        }
                    }
                }
            } catch (err) {
                this.log(`❌ ${check.name} 检查异常: ${err.message}`, 'ERROR');
                allPassed = false;
            }
        }

        return allPassed;
    }

    async checkNodeVersion() {
        try {
            const version = process.version;
            const majorVersion = parseInt(version.split('.')[0].substring(1));
            
            if (majorVersion >= 18) {
                return { success: true, message: `Node.js ${version}` };
            } else {
                return { 
                    success: false, 
                    message: `Node.js版本过低 (${version})，需要v18+`,
                    autoFix: async () => {
                        return { 
                            success: false, 
                            message: '请手动安装 Node.js v18+ 从 https://nodejs.org/' 
                        };
                    }
                };
            }
        } catch (err) {
            return { success: false, message: `Node.js检测失败: ${err.message}` };
        }
    }

    async checkMainDependencies() {
        if (this.isPackaged) {
            return { success: true, message: '已打包，跳过依赖检查' };
        }
        const requiredModules = ['express', 'socket.io', 'cap', 'protobufjs', 'winston'];
        const missingModules = [];

        for (const module of requiredModules) {
            try {
                require.resolve(module);
            } catch (err) {
                missingModules.push(module);
            }
        }

        if (missingModules.length === 0) {
            return { success: true, message: '所有依赖已安装' };
        } else {
            return {
                success: false,
                message: `缺少依赖: ${missingModules.join(', ')}`,
                autoFix: async () => this.installMainDependencies()
            };
        }
    }

    async checkNetworkPermissions() {
        return new Promise((resolve) => {
            const server = http.createServer();
            server.listen(this.config.serverPort, '127.0.0.1', () => {
                server.close(() => {
                    resolve({ success: true, message: `端口 ${this.config.serverPort} 可用` });
                });
            });
            
            server.on('error', (err) => {
                if (err.code === 'EADDRINUSE') {
                    resolve({
                        success: false,
                        message: `端口 ${this.config.serverPort} 被占用`,
                        autoFix: async () => this.killPortProcess(this.config.serverPort)
                    });
                } else {
                    resolve({ success: false, message: `网络权限检查失败: ${err.message}` });
                }
            });
        });
    }

    // 查找可用端口，优先使用配置端口，不可用则递增查找
    findAvailablePort(preferredPort, maxTries = 50) {
        const tryPort = (port) => new Promise((resolve) => {
            const tester = net.createServer()
                .once('error', () => resolve(null))
                .once('listening', () => tester.close(() => resolve(port)))
                .listen(port, '127.0.0.1');
        });
        return new Promise(async (resolve) => {
            let port = preferredPort || 8989;
            for (let i = 0; i < maxTries; i++) {
                const ok = await tryPort(port);
                if (ok) return resolve(ok);
                port++;
            }
            // 兜底使用 0 让系统分配
            const server = net.createServer();
            server.listen(0, '127.0.0.1', () => {
                const assigned = server.address().port;
                server.close(() => resolve(assigned));
            });
            server.on('error', () => resolve(preferredPort || 8989));
        });
    }

    async ensureServerPortAvailable() {
        const available = await this.findAvailablePort(this.config.serverPort);
        if (available !== this.config.serverPort) {
            this.log(`端口 ${this.config.serverPort} 不可用，自动切换到可用端口 ${available}`);
            this.config.serverPort = available;
            this.saveConfig();
        }
    }

    async checkOverlayDependencies() {
        if (this.isPackaged) {
            return { success: true, message: '使用系统浏览器应用窗口，跳过悬浮窗依赖检查' };
        }
        const overlayDir = path.join(this.appDir, 'floating_overlay');
        const electronPath = path.join(overlayDir, 'node_modules', 'electron');
        
        if (fs.existsSync(electronPath)) {
            return { success: true, message: '悬浮窗依赖已安装' };
        } else {
            return {
                success: false,
                message: '悬浮窗依赖缺失',
                autoFix: async () => this.installOverlayDependencies()
            };
        }
    }

    // 自动修复功能
    async installMainDependencies() {
        try {
            this.log('正在安装主项目依赖...');
            return new Promise((resolve) => {
                const npmCommand = os.platform() === 'win32' ? 'npm.cmd' : 'npm';
                const installProcess = spawn(npmCommand, ['install', '--registry=https://registry.npmmirror.com/'], {
                    cwd: this.appDir,
                    stdio: 'pipe'
                });

                let output = '';
                installProcess.stdout.on('data', (data) => {
                    output += data.toString();
                });
                
                installProcess.stderr.on('data', (data) => {
                    output += data.toString();
                });

                installProcess.on('close', (code) => {
                    if (code === 0) {
                        this.log('主项目依赖安装成功');
                        resolve({ success: true, message: '依赖安装完成' });
                    } else {
                        this.log(`依赖安装失败 (code: ${code}): ${output}`, 'ERROR');
                        resolve({ success: false, message: `安装失败: ${output}` });
                    }
                });
            });
        } catch (err) {
            return { success: false, message: `安装异常: ${err.message}` };
        }
    }

    async installOverlayDependencies() {
        try {
            const overlayDir = path.join(this.appDir, 'floating_overlay');
            if (!fs.existsSync(overlayDir)) {
                return { success: false, message: '悬浮窗目录不存在' };
            }

            this.log('正在安装悬浮窗依赖...');
            return new Promise((resolve) => {
                const npmCommand = os.platform() === 'win32' ? 'npm.cmd' : 'npm';
                const installProcess = spawn(npmCommand, ['install', '--registry=https://registry.npmmirror.com/'], {
                    cwd: overlayDir,
                    stdio: 'pipe'
                });

                installProcess.on('close', (code) => {
                    if (code === 0) {
                        this.log('悬浮窗依赖安装成功');
                        resolve({ success: true, message: '悬浮窗依赖安装完成' });
                    } else {
                        this.log(`悬浮窗依赖安装失败 (code: ${code})`, 'WARN');
                        resolve({ success: false, message: '悬浮窗依赖安装失败（不影响主功能）' });
                    }
                });
            });
        } catch (err) {
            return { success: false, message: `悬浮窗依赖安装异常: ${err.message}` };
        }
    }

    async killPortProcess(port) {
        try {
            if (os.platform() === 'win32') {
                execSync(`netstat -ano | findstr :${port}`, { encoding: 'utf8' });
                execSync(`for /f "tokens=5" %a in ('netstat -ano ^| find ":${port}"') do taskkill /f /pid %a`, { shell: true });
            } else {
                execSync(`lsof -ti:${port} | xargs kill -9`);
            }
            return { success: true, message: `端口 ${port} 已释放` };
        } catch (err) {
            return { success: false, message: `端口释放失败: ${err.message}` };
        }
    }

    // 递归复制资源到外部运行目录
    copyAssetRecursive(src, dest) {
        if (!fs.existsSync(src)) {
            this.log(`源文件不存在: ${src}`, 'WARN');
            return;
        }
        const stat = fs.statSync(src);
        if (stat.isDirectory()) {
            if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
            for (const entry of fs.readdirSync(src)) {
                this.copyAssetRecursive(path.join(src, entry), path.join(dest, entry));
            }
        } else {
            const dir = path.dirname(dest);
            if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
            try { 
                fs.copyFileSync(src, dest); 
            } catch (err) {
                this.log(`复制文件失败: ${src} -> ${dest} - ${err.message}`, 'ERROR');
                throw err;
            }
        }
    }

    // 准备外部运行目录，避免原生模块ABI不匹配
    prepareExternalRuntime() {
        // 优先使用与 exe 同目录的 sidecar runtime（便携式部署更直观），不存在则落到 LOCALAPPDATA
        const sidecarRuntime = path.join(this.appDir, 'runtime');
        const runtimeRoot = fs.existsSync(sidecarRuntime) ? sidecarRuntime : path.join(osPaths.localAppData, 'StarDPS', 'runtime');
        try { fs.mkdirSync(runtimeRoot, { recursive: true }); } catch {}
        const items = ['server.js', 'public', 'algo', 'skill_names.json', 'skill_names_v2.json', 'nicknames.json', 'package.json', 'package-lock.json'];
        const getAssetPath = (rel) => {
            if (!this.isPackaged) return path.join(this.appDir, rel);
            const dir = path.dirname(__filename);
            const runtimeSidecar = path.join(this.appDir, 'runtime', rel);
            const candidates = [
                runtimeSidecar,
                path.join(dir, rel),
                path.join(dir, 'temp_build', rel),
                path.join(dir, '..', rel)
            ];
            for (const p of candidates) {
                if (fs.existsSync(p)) return p;
            }
            return candidates[0];
        };
        
        this.log(`准备外部运行目录: ${runtimeRoot}`);
        for (const rel of items) {
            const src = getAssetPath(rel);
            const dest = path.join(runtimeRoot, rel);
            this.log(`复制资源: ${rel} (${src} -> ${dest})`);
            try {
                this.copyAssetRecursive(src, dest);
                if (fs.existsSync(dest)) {
                    this.log(`✅ 成功复制: ${rel}`);
                } else {
                    this.log(`❌ 复制失败: ${rel}`, 'WARN');
                }
            } catch (err) {
                this.log(`❌ 复制异常: ${rel} - ${err.message}`, 'ERROR');
            }
        }
        return runtimeRoot;
    }

    // 设备选择
    async selectNetworkDevice() {
        if (this.config.lastDeviceIndex >= 0 && !this.config.skipIntro) {
            this.log(`使用上次选择的设备: ${this.config.lastDeviceIndex}`);
            this.config.deviceIndex = this.config.lastDeviceIndex;
            return;
        }

        // 在打包模式下，自动选择默认设备，避免交互
        if (this.isPackaged) {
            this.log(`打包模式：自动选择设备 ${this.config.deviceIndex}`);
            // 确保打包模式下也会生成/更新配置文件
            this.saveConfig();
            return;
        }

        // 开发模式下可以保留设备选择交互
        const cap = require('cap');
        const devices = cap.deviceList();
        
        this.log('可用网络设备:');
        devices.forEach((device, index) => {
            this.log(`  ${index}. ${device.description}`);
        });

        // 简化：直接使用配置中的设备索引
        if (this.config.deviceIndex < devices.length) {
            this.log(`使用设备 ${this.config.deviceIndex}: ${devices[this.config.deviceIndex].description}`);
            this.config.lastDeviceIndex = this.config.deviceIndex;
            this.saveConfig();
        } else {
            this.config.deviceIndex = 0; // 默认使用第一个设备
            this.log(`设备索引超出范围，使用默认设备 0: ${devices[0].description}`);
        }
    }

    // 启动服务器
    async startServer() {
        try {
            this.log('启动DPS服务器...');
            
            // 创建自动输入配置
            const configInput = `${this.config.deviceIndex}\n${this.config.logLevel}\n`;
            const inputFile = path.join(this.appDir, 'server_input.txt');
            fs.writeFileSync(inputFile, configInput);

            const serverScript = path.join(this.appDir, 'server.js');
            if (this.isPackaged) {
                // 打包环境：释放到外部运行目录并用系统 Node 启动，确保原生模块 cap 与系统 Node ABI 一致
                const runtimeDir = this.prepareExternalRuntime();
                const { nodeExe, npmCmd } = this.findSystemNodeAndNpm();
                const needInstall = !fs.existsSync(path.join(runtimeDir, 'node_modules', 'express')) ||
                                    !fs.existsSync(path.join(runtimeDir, 'node_modules', 'cap'));
                if (needInstall) {
                    this.log('首次运行，正在为外部运行目录安装依赖...');
                    await new Promise((resolve) => {
                        const p = spawn(npmCmd, ['install', '--registry=https://registry.npmmirror.com/', '--no-fund', '--no-audit'], {
                            cwd: runtimeDir,
                            stdio: 'inherit'
                        });
                        p.on('close', () => resolve());
                        p.on('error', () => resolve());
                    });
                }
                const spawnArgs = ['server.js', `--device=${this.config.deviceIndex}`, `--log=${this.config.logLevel}`, `--port=${this.config.serverPort}`];
                this.serverExited = false;
                this.serverProcess = spawn(nodeExe, spawnArgs, {
                    cwd: runtimeDir,
                    stdio: ['pipe', 'pipe', 'pipe']
                });
                this.serverProcess.stdout.on('data', (data) => {
                    const output = data.toString().trim();
                    if (output) this.log(`[Server] ${output}`);
                });
                this.serverProcess.stderr.on('data', (data) => {
                    const output = data.toString().trim();
                    if (output) this.log(`[Server Error] ${output}`, 'WARN');
                });
                this.serverProcess.on('close', (code) => {
                    this.serverExited = true;
                    this.log(`服务器进程退出 (code: ${code})`, 'WARN');
                });
            } else {
                // 开发环境：作为子进程启动
                const spawnArgs = [serverScript, `--device=${this.config.deviceIndex}`, `--log=${this.config.logLevel}`, `--port=${this.config.serverPort}`];
                this.serverProcess = spawn('node', spawnArgs, {
                    cwd: this.appDir,
                    stdio: ['pipe', 'pipe', 'pipe']
                });

                this.serverProcess.stdout.on('data', (data) => {
                    const output = data.toString().trim();
                    if (output) {
                        this.log(`[Server] ${output}`);
                    }
                });

                this.serverProcess.stderr.on('data', (data) => {
                    const output = data.toString().trim();
                    if (output) {
                        this.log(`[Server Error] ${output}`, 'WARN');
                    }
                });

                this.serverProcess.on('close', (code) => {
                    this.log(`服务器进程退出 (code: ${code})`, 'WARN');
                    this.serverProcess = null;
                });
            }

            // 等待服务器启动
            await this.waitForServer();
            this.log('✅ DPS服务器启动成功');
            
            return true;
        } catch (err) {
            this.log(`服务器启动失败: ${err.message}`, 'ERROR');
            return false;
        }
    }

    async waitForServer() {
        const maxAttempts = 60;
        let attempts = 0;

        return new Promise((resolve, reject) => {
            const checkServer = () => {
                if (this.serverExited) {
                    return reject(new Error('服务器进程已退出'));
                }
                const req = http.get(`http://localhost:${this.config.serverPort}/api/data`, (res) => {
                    if (res.statusCode === 200) {
                        resolve();
                    } else {
                        scheduleNext();
                    }
                }).on('error', () => {
                    scheduleNext();
                });

                const scheduleNext = () => {
                    attempts++;
                    if (attempts < maxAttempts) {
                        setTimeout(checkServer, 1000);
                    } else {
                        reject(new Error('服务器启动超时'));
                    }
                };
            };

            checkServer();
        });
    }

    // 启动浏览器
    async startBrowser() {
        if (!this.config.autoStartBrowser) {
            this.log('浏览器自动启动已禁用');
            return;
        }

        try {
            this.log('启动浏览器客户端...');
            const url = `http://localhost:${this.config.serverPort}`;

            if (os.platform() === 'win32') {
                const candidates = [
                    // Chrome
                    process.env['PROGRAMFILES(X86)'] && path.join(process.env['PROGRAMFILES(X86)'], 'Google', 'Chrome', 'Application', 'chrome.exe'),
                    process.env['PROGRAMFILES'] && path.join(process.env['PROGRAMFILES'], 'Google', 'Chrome', 'Application', 'chrome.exe'),
                    process.env['LOCALAPPDATA'] && path.join(process.env['LOCALAPPDATA'], 'Google', 'Chrome', 'Application', 'chrome.exe'),
                    // Edge
                    process.env['PROGRAMFILES(X86)'] && path.join(process.env['PROGRAMFILES(X86)'], 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
                    process.env['PROGRAMFILES'] && path.join(process.env['PROGRAMFILES'], 'Microsoft', 'Edge', 'Application', 'msedge.exe')
                ].filter(Boolean);
                const browserExe = this.getFirstExistingPath(candidates);
                if (browserExe) {
                    // 优先用应用模式
                    const isEdge = browserExe.toLowerCase().includes('msedge');
                    const args = isEdge ? ['--app=' + url] : ['--app=' + url, '--new-window'];
                    spawn(browserExe, args, { detached: true, stdio: 'ignore' }).unref();
                    this.log('✅ 浏览器客户端已启动（应用窗口模式）');
                    return;
                }
                // 回退到系统默认
                spawn('cmd', ['/c', 'start', '', url], { detached: true, stdio: 'ignore' }).unref();
                this.log('✅ 浏览器客户端已启动（默认浏览器）');
                return;
            }

            // macOS / Linux 简化处理
            const opener = os.platform() === 'darwin' ? 'open' : 'xdg-open';
            spawn(opener, [url], { detached: true, stdio: 'ignore' }).unref();
            this.log('✅ 浏览器客户端已启动');
        } catch (err) {
            this.log(`浏览器启动异常: ${err.message}`, 'WARN');
        }
    }

    // 启动悬浮窗
    async startOverlay() {
        if (!this.config.autoStartOverlay) {
            this.log('悬浮窗自动启动已禁用');
            return;
        }

        try {
            // 打包环境：优先启动 Electron 悬浮窗（sidecar runtime 下），失败再用 Edge/Chrome 应用窗口
            if (this.isPackaged) {
                const url = `http://localhost:${this.config.serverPort}`;
                // 1) 尝试启动 Electron 悬浮窗
                try {
                    const runtimeDir = this.prepareExternalRuntime();
                    const overlayDir = path.join(runtimeDir, 'floating_overlay');
                    const electronCandidates = [
                        path.join(overlayDir, 'node_modules', 'electron', 'dist', 'electron.exe'),
                        path.join(overlayDir, 'node_modules', '.bin', 'electron.cmd')
                    ];
                    const electronExe = this.getFirstExistingPath(electronCandidates);
                    const overlayMain = path.join(overlayDir, 'main.js');
                    if (electronExe && fs.existsSync(overlayMain)) {
                        this.log('检测到 Electron 悬浮窗，尝试启动...');
                        spawn(electronExe, [overlayMain, '--no-sandbox'], {
                            cwd: overlayDir,
                            detached: true,
                            stdio: 'ignore'
                        }).unref();
                        this.overlayStarted = true;
                        this.log('✅ Electron 悬浮窗已启动');
                        return;
                    } else if (fs.existsSync(overlayDir)) {
                        // 自动安装悬浮窗依赖
                        this.log('安装 Electron 悬浮窗依赖...');
                        const { npmCmd } = this.findSystemNodeAndNpm();
                        try {

                            // 使用 spawn 避免 Windows 路径带空格（如 C:\\Program Files\\nodejs\\npm.cmd）导致的命令解析失败
                            await new Promise((resolve) => {
                                const p = spawn(npmCmd, ['install', '--registry=https://registry.npmmirror.com/','--electron-mirror=https://npmmirror.com/mirrors/electron/', '--no-fund', '--no-audit'], {
                                    cwd: overlayDir,
                                    stdio: 'inherit'
                                });
                                p.on('close', () => resolve());
                                p.on('error', () => resolve());

                            });
                            const electronExe2 = this.getFirstExistingPath(electronCandidates);
                            if (electronExe2 && fs.existsSync(overlayMain)) {
                                spawn(electronExe2, [overlayMain, '--no-sandbox'], {
                                    cwd: overlayDir,
                                    detached: true,
                                    stdio: 'ignore'
                                }).unref();
                                this.overlayStarted = true;
                                this.log('✅ Electron 悬浮窗已启动');
                                return;
                            }
                        } catch (e) {
                            this.log(`Electron 依赖安装失败: ${e.message}`, 'WARN');
                        }
                    }
                } catch (e) {
                    this.log(`Electron 悬浮窗启动异常: ${e.message}`, 'WARN');
                }

                // 2) 回退：Edge/Chrome 应用窗口（仅当允许启动浏览器且悬浮窗未启动）
                const candidates = [
                    // Edge
                    process.env['PROGRAMFILES(X86)'] && path.join(process.env['PROGRAMFILES(X86)'], 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
                    process.env['PROGRAMFILES'] && path.join(process.env['PROGRAMFILES'], 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
                    // Chrome
                    process.env['PROGRAMFILES(X86)'] && path.join(process.env['PROGRAMFILES(X86)'], 'Google', 'Chrome', 'Application', 'chrome.exe'),
                    process.env['PROGRAMFILES'] && path.join(process.env['PROGRAMFILES'], 'Google', 'Chrome', 'Application', 'chrome.exe'),
                    process.env['LOCALAPPDATA'] && path.join(process.env['LOCALAPPDATA'], 'Google', 'Chrome', 'Application', 'chrome.exe')
                ].filter(Boolean);
                const appExe = this.getFirstExistingPath(candidates);
                if (appExe && this.config.autoStartBrowser && !this.overlayStarted) {
                    const isEdge = appExe.toLowerCase().includes('msedge');
                    const args = isEdge ? ['--app=' + url] : ['--app=' + url, '--new-window'];
                    spawn(appExe, args, { detached: true, stdio: 'ignore' }).unref();
                    this.log(`✅ 以 ${isEdge ? 'Edge' : 'Chrome'} 应用窗口方式启动桌面端`);
                    return;
                }
                if (this.config.autoStartBrowser && !this.overlayStarted) {
                    await this.startBrowser();
                    this.log('已回退到默认浏览器窗口');
                }
                return;
            }

            // 开发环境且安装了 Electron 时，使用原生悬浮窗
            const overlayDir = path.join(this.appDir, 'floating_overlay');
            const electronPath = path.join(overlayDir, 'node_modules', 'electron', 'dist', 'electron.exe');
            const mainScript = path.join(overlayDir, 'main.js');

            if (!fs.existsSync(electronPath) || !fs.existsSync(mainScript)) {
                this.log('悬浮窗文件缺失，跳过启动', 'WARN');
                return;
            }

            this.log('启动桌面悬浮窗...');
            this.overlayProcess = spawn(electronPath, [mainScript, '--no-sandbox'], {
                cwd: overlayDir,
                stdio: 'pipe'
            });

            this.overlayProcess.stdout.on('data', (data) => {
                const output = data.toString().trim();
                if (output) {
                    this.log(`[Overlay] ${output}`);
                }
            });

            this.overlayProcess.stderr.on('data', (data) => {
                const output = data.toString().trim();
                if (output && !output.includes('DevTools')) {
                    this.log(`[Overlay] ${output}`, 'WARN');
                }
            });

            this.overlayProcess.on('close', (code) => {
                this.log(`悬浮窗进程退出 (code: ${code})`, 'INFO');
                this.overlayProcess = null;
            });

            this.log('✅ 桌面悬浮窗已启动');

        } catch (err) {
            this.log(`悬浮窗启动失败: ${err.message}`, 'WARN');
        }
    }

    // 显示状态信息
    showStatus() {
        console.log('\n' + '='.repeat(60));
        console.log(`     ${this.appName} v${this.version}`);
        console.log('='.repeat(60));
        console.log('🎯 状态信息:');
        console.log(`   服务器: http://localhost:${this.config.serverPort}`);
        console.log(`   服务器进程: ${this.serverProcess ? '运行中' : '未启动'}`);
        console.log(`   悬浮窗进程: ${this.overlayProcess ? '运行中' : '未启动'}`);
        console.log(`   日志文件: ${this.logFile}`);
        console.log('\n🎮 使用说明:');
        console.log('   1. 启动游戏《星痕共鸣》');
        console.log('   2. 进入战斗开始数据采集');
        console.log('   3. 在浏览器中查看实时DPS数据');
        console.log('   4. 桌面悬浮窗显示简化信息');
        console.log('\n⚠️  注意事项:');
        console.log('   - 需要管理员权限进行网络包捕获');
        console.log('   - 确保防火墙允许程序访问网络');
        console.log('   - 关闭此窗口将停止所有服务');
        console.log('='.repeat(60));
        console.log('\n按 Ctrl+C 退出程序\n');
    }

    // 清理和退出
    async cleanup() {
        this.log('正在清理进程...');

        const processes = [
            { name: '服务器', process: this.serverProcess },
            { name: '悬浮窗', process: this.overlayProcess }
        ];

        for (const { name, process } of processes) {
            if (process && !process.killed) {
                this.log(`停止${name}进程...`);
                process.kill('SIGTERM');
                
                // 给进程一些时间优雅退出
                setTimeout(() => {
                    if (!process.killed) {
                        process.kill('SIGKILL');
                    }
                }, 3000);
            }
        }

        this.log('程序退出');
        process.exit(0);
    }

    // 主启动流程
    async run() {
        try {
            console.log(`\n🚀 启动 ${this.appName} v${this.version}...\n`);

            // 1. 环境检测和修复
            const envOk = await this.checkEnvironment();
            if (!envOk) {
                console.log('\n❌ 环境检测未通过，部分功能可能受限');
                console.log('详细信息请查看日志:', this.logFile);
            }

            // 2. 选择网络设备
            await this.selectNetworkDevice();

            // 3. 确保端口可用（如被占用自动更换并保存配置）
            await this.ensureServerPortAvailable();

            // 4. 启动服务器
            const serverStarted = await this.startServer();
            if (!serverStarted) {
                throw new Error('服务器启动失败');
            }

            // 5. 先启动悬浮窗（Electron 优先）
            await this.startOverlay();

            // 6. 如需要再启动浏览器
            if (this.config.autoStartBrowser) {
                await this.startBrowser();
            }

            // 7. 显示状态信息
            this.showStatus();

            // 8. 处理退出信号
            process.on('SIGINT', () => this.cleanup());
            process.on('SIGTERM', () => this.cleanup());
            process.on('exit', () => this.cleanup());

        } catch (err) {
            this.log(`启动失败: ${err.message}`, 'ERROR');
            console.error(`\n❌ 启动失败: ${err.message}`);
            console.log(`详细错误信息请查看日志: ${this.logFile}`);
            
            // 显示常见解决方案
            console.log('\n🔧 常见解决方案:');
            console.log('  1. 以管理员身份运行程序');
            console.log('  2. 检查网络连接和防火墙设置');
            console.log('  3. 确保没有其他程序占用端口8989');
            console.log('  4. 重新安装 Node.js (版本 18+)');
            
            process.exit(1);
        }
    }
}

// 程序入口
if (require.main === module) {
    const launcher = new DPSTrackerLauncher();
    launcher.run().catch((err) => {
        console.error('程序启动异常:', err);
        process.exit(1);
    });
}

module.exports = DPSTrackerLauncher;
